
package de.justphil.tcg.tcgandroidclient.network.rest;

import java.util.List;

import de.justphil.tcg.tcgandroidclient.content.rest.RESTCreateGameResponse;
import de.justphil.tcg.tcgandroidclient.content.rest.RESTFoundMsgData;
import de.justphil.tcg.tcgandroidclient.content.rest.RESTGeoCard;
import de.justphil.tcg.tcgandroidclient.content.rest.RESTGeoRq;
import de.justphil.tcg.tcgandroidclient.content.rest.RESTJoinMsgData;
import de.justphil.tcg.tcgandroidclient.content.rest.RESTGameReadyResponse;
import de.justphil.tcg.tcgandroidclient.network.rest.Http.Method;
import de.justphil.tcg.tcgandroidclient.util.GeoCalculation;
import de.justphil.tcg.tcgandroidclient.util.StringTools;
import de.justphil.tcg.tcgandroidclient.util.TCGLog;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;

public class RESTMessengerService extends Service {
	
	public class RESTBinder extends Binder {
		
		public Messenger getMessenger(Messenger callBackMessenger) {
		
			if (callBackMessenger == null) {
				throw new IllegalArgumentException("The callBackMessenger must not be null!");
			}
			RESTMessengerService.this.callBackMessenger = callBackMessenger;
			return messenger;
		}
	}
	
	public final IBinder binder = new RESTBinder();
	public static final int MSG_CREATE = 1;
	public static final int MSG_CREATED = 2;
	public static final int MSG_JOINED = 3;
	public static final int MSG_JOIN = 4;
	public static final int MSG_ERROR = 5;
	public static final int MSG_LOG_IN = 6;
	public static final int MSG_LOGGED_IN = 7;
	public static final int MSG_NOT_RESPONDING = 8;
	public static final int MSG_GEO_RQ = 9;
	public static final int MSG_GEO_RESPONSE = 10;
	public static final int MSG_FOUND_RQ = 11;
	public static final int MSG_FOUND_RESPONSE = 12;
	private Messenger messenger;
	private Messenger callBackMessenger;
	
	// private TCGContentAdapter contentAdapter;
	@Override
	public void onCreate() {
	
		super.onCreate();
		HandlerThread thread = new HandlerThread("RestMessengerThread");
		thread.start();
		// contentAdapter = new TCGContentAdapter(this);
		messenger = new Messenger(new Handler(thread.getLooper()) {
			
			@Override
			public void handleMessage(Message msg) {
			
				RESTMessengerService.this.handleMessage(msg);
			}
		});
	}
	
	@Override
	public IBinder onBind(Intent intent) {
	
		return binder;
	}
	
	private void handleMessage(Message msg) {
	
		switch (msg.what) {
			case MSG_CREATE:
				handleCreateMsg(msg);
				break;
			case MSG_JOIN:
				handleJoinMsg(msg);
				break;
			case MSG_LOG_IN:
				handleLogInMsg(msg);
				break;
			case MSG_GEO_RQ:
				handleGeoRqMsg(msg);
				break;
			case MSG_FOUND_RQ:
				handleFoundRqMsg(msg);
				break;
			default:
				throw new IllegalArgumentException("Message type unknown!");
		}
	}
	
	private void handleFoundRqMsg(Message msg) {
	
		try {
			Thread.sleep(2000);
		}
		catch (InterruptedException e1) {
		}
		RESTFoundMsgData data = (RESTFoundMsgData) msg.obj;
		String urlPath = StringTools.getString(new Object[] {
				"/card/freebie/", data.getLat() + "/", data.getLon(), "/", data.getCardId() });
		
		String body = RESTEncoder.encodeFoundRq(data.getHash());

		ResponseContainer rc = RESTHelper.restRequest(this, urlPath, body,null, Method.PUT);
		if (rc == null) {
			sendNotRespondingMsg();
			return;
		}
		Message responseMsg;
		if (rc.getResponseCode() == RESTHelper.CODE_OK) {
			responseMsg = Message.obtain(null, MSG_FOUND_RESPONSE);
		}
		else {
			responseMsg = getErrorMsg(rc);
		}
		try {
			callBackMessenger.send(responseMsg);
		}
		catch (RemoteException e) {
			throw new RuntimeException("Could not send message: " + e.getMessage());
		}
	}
	
	private void handleGeoRqMsg(Message msg) {
	
		RESTGeoRq geoRq = (RESTGeoRq) msg.obj;
		TCGLog.d(new Object[] {
			"MS Handling GeoRqMsg." });
		double latitude = geoRq.getLatitude();
		double longitude = geoRq.getLongitude();
		
		String urlPath = StringTools.getString(new Object[] {
				"/card/freebies/", latitude + "/", longitude, "/", geoRq.getRadius() });
		ResponseContainer rc = RESTHelper.restRequest(this, urlPath, null, Method.GET);
		if (rc == null) {
			sendNotRespondingMsg();
			return;
		}
		Message responseMsg;
		if (rc.getResponseCode() == RESTHelper.CODE_OK) {
			List<RESTGeoCard> card = RESTDecoder.decodeGeoResponse(RESTHelper.getBodyStr(rc));
			responseMsg = Message.obtain(null, MSG_GEO_RESPONSE, card);
		}
		else {
			responseMsg = getErrorMsg(rc);
		}
		try {
			callBackMessenger.send(responseMsg);
		}
		catch (RemoteException e) {
			throw new RuntimeException("Could not send message: " + e.getMessage());
		}
	}
	
	private void handleLogInMsg(Message msg) {
	
		TCGLog.d(new Object[] {
			"MS Handling LogInMsg." });
		String urlPath = "/user/auth";
		ResponseContainer rc = RESTHelper.restRequest(this, urlPath, null, Method.GET);
		if (rc == null) {
			sendNotRespondingMsg();
			return;
		}
		Message responseMsg;
		if (rc.getResponseCode() == RESTHelper.CODE_OK) {
			long playerId = RESTDecoder.decodeLogInResponse(RESTHelper.getBodyStr(rc));
			responseMsg = Message.obtain(null, MSG_LOGGED_IN, playerId);
		}
		else {
			responseMsg = getErrorMsg(rc);
		}
		try {
			callBackMessenger.send(responseMsg);
		}
		catch (RemoteException e) {
			throw new RuntimeException("Could not send message: " + e.getMessage());
		}
	}
	
	private void sendNotRespondingMsg() {
	
		TCGLog.w(new Object[] {
			"REST request failed!" });
		try {
			callBackMessenger.send(Message.obtain(null, MSG_NOT_RESPONDING));
		}
		catch (RemoteException e) {
			throw new RuntimeException("Could not send Message!");
		}
	}
	
	private void handleJoinMsg(Message msg) {
	
		long deckId = ((RESTJoinMsgData) msg.obj).getDeckId();
		String otp = ((RESTJoinMsgData) msg.obj).getOtp();
		TCGLog.d(new Object[] {
			"MS Handling JoinMsg." });
		try {
			Thread.sleep(1000);
		}
		catch (InterruptedException ignored) {
		}
		String urlPath = "/game/new/" + otp;
		String body = RESTEncoder.encodeJoinGameRq(deckId);
		ResponseContainer rc = RESTHelper.restRequest(this, urlPath, body, null, Method.PUT);
		if (rc == null) {
			sendNotRespondingMsg();
			return;
		}
		Message responseMsg;
		if (rc.getResponseCode() == RESTHelper.CODE_OK) {
			RESTGameReadyResponse joinResponse =
					RESTDecoder.decodeJoinResponse(RESTHelper.getBodyStr(rc));
			responseMsg = Message.obtain(null, MSG_JOINED, joinResponse);
		}
		else {
			responseMsg = getErrorMsg(rc);
		}
		try {
			callBackMessenger.send(responseMsg);
		}
		catch (RemoteException e) {
			throw new RuntimeException("Could not send message: " + e.getMessage());
		}
	}
	
	private Message getErrorMsg(ResponseContainer rc) {
	
		String bodyStr = RESTHelper.getBodyStr(rc);
		String message = "";
		if (!"".equals(bodyStr)) {
			try {
				message += RESTDecoder.decodeMessage(bodyStr);
			}
			catch (Exception e) {
				message += "Could not decode Message body '" + bodyStr + "'";
			}
		}
		Message msg = Message.obtain(null, MSG_ERROR, message);
		msg.arg1 = rc.getResponseCode();
		return msg;
	}
	
	private void handleCreateMsg(Message msg) {
	
		RESTCreateGameResponse createResponse;
		{
			long deckId = (Long) msg.obj;
			TCGLog.d(new Object[] {
				"MS Handling CreateMsg." });
			try {
				Thread.sleep(1000);
			}
			catch (InterruptedException ignored) {
			}
			String urlPath = "/game/new";
			String body = RESTEncoder.encodeCreateGameRq(deckId);
			ResponseContainer rc = RESTHelper.restRequest(this, urlPath, body, null, Method.POST);
			if (rc == null) {
				sendNotRespondingMsg();
				return;
			}
			Message responseMsg;
			if (rc.getResponseCode() == RESTHelper.CODE_CREATED) {
				createResponse = RESTDecoder.decodeCreateGameResult(RESTHelper.getBodyStr(rc));
				responseMsg = Message.obtain(null, MSG_CREATED, createResponse);
				try {
					callBackMessenger.send(responseMsg);
				}
				catch (RemoteException e) {
					throw new RuntimeException("Could not send message: " + e.getMessage());
				}
			}
			else {
				responseMsg = getErrorMsg(rc);
				try {
					callBackMessenger.send(responseMsg);
				}
				catch (RemoteException e) {
					throw new RuntimeException("Could not send message: " + e.getMessage());
				}
				return;
			}
		}
		{
			boolean ok = false;
			ResponseContainer rc = null;
			while (!ok) {
				try {
					Thread.sleep(createResponse.getPollInterval() * 1000);
				}
				catch (InterruptedException ignored) {
				}
				String urlPath = "/game/new/" + createResponse.getOtp();
				rc = RESTHelper.restRequest(this, urlPath, null, Method.GET);
				if (rc == null) {
					sendNotRespondingMsg();
					return;
				}
				switch (rc.getResponseCode()) {
					case RESTHelper.CODE_NOT_MODIFIED:
						/* Keep looping. */
						break;
					case RESTHelper.CODE_OK:
						ok = true;
						break;
					default: {
						Message responseMsg = getErrorMsg(rc);
						try {
							callBackMessenger.send(responseMsg);
						}
						catch (RemoteException e) {
							throw new RuntimeException("Could not send message: " + e.getMessage());
						}
						return;
					}
				}
			}
			RESTGameReadyResponse joinedResponse =
					RESTDecoder.decodePlayerJoinedResponse(RESTHelper.getBodyStr(rc));
			Message responseMsg = Message.obtain(null, MSG_JOINED, joinedResponse);
			try {
				callBackMessenger.send(responseMsg);
			}
			catch (RemoteException e) {
				throw new RuntimeException("Could not send message: " + e.getMessage());
			}
		}
	}
}